//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.TridentModel
{
    #region Using Directives
    using System;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text.RegularExpressions;
    using System.Workflow.ComponentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.DataLayer;
    using Activity = System.Workflow.ComponentModel.Activity;
    using ErActivity = Microsoft.Research.DataLayer.Activity;
    using Microsoft.Research.eResearch.Common.Linq;
    using System.Drawing;
    #endregion

    /// <summary>
    /// This is the base Model for all UI related changes.
    /// </summary>
    [Serializable()]
    public class TridentWorkflowModel
    {
        #region Private Member Data

        /// <summary>
        /// ID of the workflow.
        /// </summary>
        private Guid id;

        /// <summary>
        /// This holds Name of the Workflow.
        /// </summary>
        private string name;

        /// <summary>
        /// This holds Description of the Workflow.
        /// </summary>
        private string description;

        /// <summary>
        /// List of category Model.
        /// </summary>
        private WorkflowCategoryModel category;

        /// <summary>
        /// Children Activity Models.
        /// </summary>
        private BaseCompositeModel root;

        /// <summary>
        /// XOML content of the workflow.
        /// </summary>
        private string xomlContent;

        /// <summary>
        /// The reference activity for the workflow.
        /// </summary>
        [NonSerialized]
        private Activity blackboxActivity;

        /// <summary>
        /// Provides a flat listing of the activities in the workflow.
        /// </summary>
        private Hashtable modelsHashtable = new Hashtable();

        /// <summary>
        /// The assemblies used by the workflow.
        /// </summary>
        private Collection<AssemblyInfoModel> workflowAssociatedAssemblies = new Collection<AssemblyInfoModel>();

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor for the workflow model.
        /// </summary>
        /// <param name="rootActivity">E.R. Activity.</param>
        public TridentWorkflowModel(ErActivity rootActivity, ModelFactory modelFactory)
        {
            if (null != rootActivity && modelFactory != null && rootActivity.IsWorkflow)
            {
                // Name of the workflow.
                this.name = rootActivity.Name;

                // Description.
                this.description = rootActivity.Description;

                // Id of the workflow.
                this.id = ((IObject)rootActivity).ID;

                // populate the categories
                if (rootActivity.Namespaces.Count > 0)
                {
                    Namespace category = rootActivity.Namespaces[0];
                    WorkflowCategoryModel wfCategoryModel = new WorkflowCategoryModel(category, true);
                    this.category = wfCategoryModel;
                }

                this.root = modelFactory.FetchModelByActivitySequence(rootActivity.Children[0], null) as BaseCompositeModel;
                this.root.ChildrenCollectionChanged += new EventHandler<NotifyCollectionChangedEventArgs>(this.OnRootActivityModelsCollectionChanged);
                this.root.Children.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnRootActivityModelsCollectionChanged);
                this.UpdateModelsHash(this.root);

                //this.PopulateSubsectionList(rootActivity.BaseSequence);
                this.BindSwapSubsectionStateEventHandler(this.root);

                // Set the xoml content.
                this.xomlContent = rootActivity.XomlContents;

                this.IsInteractive = rootActivity.IsInteractive;
                this.SavedIsInteractiveValue = rootActivity.IsInteractive;
                this.IsLoadedFromRegistry = true;
                this.VersionLabel = rootActivity.VersionLabel;
                this.WorkflowImage = rootActivity.Icon;

                this.Owner = new SecurityPrincipalModel(rootActivity.Owner).DisplayName;
            }
            else
            {
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber6000001,
                    TridentResourceManager.GetString("InvalidWorkflowObject"));
            }
        }

        /// <summary>
        /// Create a new workflow.
        /// </summary>
        public TridentWorkflowModel()
        {
            // Get the new ID for workflow.
            this.id = Guid.Empty;

            // When user is creating new workflow, current logged in user becomes the owner.
            this.Owner = new SecurityPrincipalModel(TridentAuthentication.LoggedInUser).DisplayName;
        }

        /// <summary>
        /// Create the workflow model from the ComponentModel.Activity object.
        /// </summary>
        /// <param name="activity">
        /// The Activity from which to create the workflow model.
        /// </param>
        /// <param name="wfName">
        /// Name of the workflow.
        /// </param>
        /// <param name="wfDesc">
        /// Description of the workflow.
        /// </param>
        /// <param name="categories">
        /// The categories list to which this workflow belongs.
        /// </param>
        /// <param name="xomlString">
        /// The xoml represented by this workflow.
        /// </param>
        /// <param name="rulesString">
        /// The rules for this workflow.
        /// </param>
        public TridentWorkflowModel(
            Activity activity,
            ModelFactory modelFactory,
            string workflowName,
            string workflowDescription,
            string xoml)
        {
            CompositeActivity rootActivity = activity as CompositeActivity;

            // Check if the root activity is of type composite activity.
            if (rootActivity != null && modelFactory != null)
            {
                // Set the name.
                this.name = workflowName;

                // Set the description.
                this.description = workflowDescription;


                // Create the ActivityModel for the root activity.
                BaseCompositeModel rootActModel = modelFactory.FetchModelByActivity(activity, null) as BaseCompositeModel;
                if (rootActModel != null)
                {
                    this.root = rootActModel;
                    this.root.ChildrenCollectionChanged += new EventHandler<NotifyCollectionChangedEventArgs>(this.OnRootActivityModelsCollectionChanged);
                    this.root.Children.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnRootActivityModelsCollectionChanged);
                    this.UpdateModelsHash(this.root);
                }

                if (this.IsBlackBox)
                {
                    this.BlackBoxActivity = activity;
                }

                // Set the xoml content.
                this.xomlContent = xoml;

                // New id for the guid.
                this.id = Guid.Empty;

                // When user is opening a workflow from file system, current logged in user becomes the owner.
                this.Owner = new SecurityPrincipalModel(TridentAuthentication.LoggedInUser).DisplayName;
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the Workflow ID.
        /// </summary>
        public Guid Id
        {
            get
            {
                return this.id;
            }
            set
            {
                this.id = value;
            }
        }

        /// <summary>
        /// Gets the workflow Name.
        /// </summary>
        public string Name
        {
            get
            {
                return this.name;
            }

            set
            {
                this.name = value;
            }
        }

        /// <summary>
        /// Gets or sets the workflow Description.
        /// </summary>
        public string Description
        {
            get
            {
                return this.description;
            }

            set
            {
                this.description = value;
            }
        }

        /// <summary>
        /// Gets or sets whether the workflow is a interactive workflow.
        /// </summary>
        public bool IsInteractive { get; set; }

        /// <summary>
        /// Gets or sets the saved value of whether the workflow is a interactive workflow.
        /// </summary>
        public bool SavedIsInteractiveValue { get; set; }

        /// <summary>
        /// Gets or sets the flag representing if the workflow is opened from registry.
        /// </summary>
        public bool IsLoadedFromRegistry { get; set; }

        /// <summary>
        /// Gets or sets the Id of the workflow to update. Required while validation of the name in the property pane.
        /// Will contain the value only in the open from file system - save as to registry scenario.
        /// </summary>
        public Guid WorkflowToUpdate { get; set; }

        /// <summary>
        /// Gets or sets the category models to which this activity has been assigned.
        /// </summary>
        public WorkflowCategoryModel Category
        {
            get
            {
                return this.category;
            }

            set
            {
                this.category = value;
            }
        }

        /// <summary>
        /// Gets or sets the workflow Children.
        /// </summary>
        public BaseCompositeModel Root
        {
            get
            {
                return this.root;
            }

            set
            {
                this.root = value;
                this.root.ChildrenCollectionChanged += new EventHandler<NotifyCollectionChangedEventArgs>(this.OnRootActivityModelsCollectionChanged);
                this.root.Children.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnRootActivityModelsCollectionChanged);
                this.UpdateModelsHash(this.root);
            }
        }

        /// <summary>
        /// Gets or sets the XOML content of the workflow.
        /// </summary>
        public string XomlContent
        {
            get
            {
                return this.xomlContent;
            }

            set
            {
                this.xomlContent = value;
            }
        }

        /// <summary>
        /// The hashtable of models' unique ids to names.
        /// </summary>
        public Hashtable ModelsHashtable
        {
            get
            {
                return this.modelsHashtable;
            }
        }

        /// <summary>
        /// Determine whether the workflow contains blackbox activities.
        /// </summary>
        public bool IsBlackBox
        {
            get
            {
                bool result = false;

                // Check if the workflow is a blackbox or not.
                foreach (BaseModel model in this.modelsHashtable.Values)
                {
                    if (model.IsSimpleBlackBoxModel || model.IsCompositeBlackBoxModel)
                    {
                        // If blackbox then store the activity within the workflow model.
                        result = true;
                        break;
                    }
                }
                return result;
            }
        }

        /// <summary>
        /// The reference activity if the workflow is blackbox.
        /// </summary>
        public Activity BlackBoxActivity
        {
            get
            {
                return this.blackboxActivity;
            }
            set
            {
                this.blackboxActivity = value;
            }
        }

        /// <summary>
        /// The display name of the activity.
        /// </summary>
        public string GetDisplayName
        {
            get
            {
                string result;
                if (string.IsNullOrEmpty(VersionLabel))
                {
                    result = this.name;
                }
                else
                {
                    result = string.Format(CultureInfo.InvariantCulture, "{0} ({1})", this.name, this.VersionLabel);
                }
                return result;
            }
        }

        /// <summary>
        /// Holds the Backup the TridentWorkflowModel Id when ever the user Clears the Id
        /// </summary>
        public Guid PreviousWorkflowId { get; set; }

        /// <summary>
        /// Gets or sets a flag which specifies whether newer versions of the workflow exist.
        /// </summary>
        public bool NewerVersionsExist { get; set; }

        /// <summary>
        /// Gets or sets the version label of the workflow.
        /// </summary>
        public string VersionLabel { get; set; }

        /// <summary>
        /// Gets or sets the workflow associated assemblies.
        /// </summary>
        /// <value>The workflow associated assemblies.</value>
        public Collection<AssemblyInfoModel> WorkflowAssociatedAssemblies
        {
            get
            {
                return this.workflowAssociatedAssemblies;
            }
            set
            {
                this.workflowAssociatedAssemblies = value;
            }
        }

        /// <summary>
        /// Sets a value indicating whether this instance is provenance mode.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is provenance mode; otherwise, <c>false</c>.
        /// </value>
        public bool IsProvenanceMode
        {
            get
            {
                bool val = false;
                if (null != this.Root)
                {
                    val = (this.Root as BaseModel).IsProvenanceMode;
                }

                return val;
            }

            set
            {
                if (null != this.Root)
                    (this.Root as BaseModel).IsProvenanceMode = value;
            }
        }

        /// <summary>
        /// Gets a value indicating whether owner or admin.
        /// </summary>
        /// <value><c>true</c> if owner or admin; otherwise, <c>false</c>.</value>
        public bool OwnerOrAdmin
        {
            get
            {
                bool owner = false;
                if (!string.IsNullOrEmpty(this.Owner) &&
                    (this.Owner.Equals(new SecurityPrincipalModel(TridentAuthentication.LoggedInUser).DisplayName, StringComparison.OrdinalIgnoreCase))
                    || (TridentAuthentication.LoggedUserRole == UserRole.Admin)
                    || (TridentAuthentication.LoggedUserRole == UserRole.Service))
                {
                    owner = true;
                }

                return owner;
            }
        }

        /// <summary>
        /// Gets or sets the workflow image.
        /// </summary>
        /// <value>The workflow image.</value>
        public Image WorkflowImage { get; set; }

        /// <summary>
        /// Gets or sets the owner.
        /// </summary>
        public string Owner { get; set; }

        #endregion

        #region Events

        /// <summary>
        /// Fired on click of the swap subsection state.
        /// </summary>
        [field: NonSerialized]
        public event EventHandler SwapSubsectionState;

        #endregion Events

        #region Public Functions


        /// <summary>
        /// Set the parent-Child relationship of the models. This is needed because after cloning the relationship is lost.
        /// </summary>
        /// <param name="workflowModel">The workflow model</param>
        public static void SetParentChildRelationship(TridentWorkflowModel workflowModel)
        {
            foreach (BaseModel model in workflowModel.modelsHashtable.Values)
            {
                // For all the composite models in the hashtable set the relationship.
                BaseCompositeModel baseCompositeModel = model as BaseCompositeModel;

                if (baseCompositeModel != null)
                {
                    foreach (BaseModel childModel in baseCompositeModel.Children)
                    {
                        childModel.ParentModel = baseCompositeModel;
                    }
                }
            }
        }

        /// <summary>
        /// Check if the workflow name contains invalid characters.
        /// </summary>
        /// <param name="workflowName">The workflow name.</param>
        /// <returns>True if the name contains valid characters; false otherwise</returns>
        public static bool CheckNameForInvalidCharacters(string workflowName)
        {
            //return Regex.IsMatch(workflowName, @"^([a-zA-Z0-9\@\s_-]+)$");
            return Regex.IsMatch(workflowName, @"^([\s\S]+)$");
        }

        /// <summary>
        /// Clears the Guid
        /// </summary>
        public void ClearId()
        {
            this.PreviousWorkflowId = this.Id;
            this.id = Guid.Empty;
        }

        /// <summary>
        /// Gets the model by name.
        /// </summary>
        /// <param name="uniqueId">
        /// The unique id by which to get the model.
        /// </param>
        /// <returns>
        /// The model which has the unique id, null otherwise.
        /// </returns>
        public BaseModel GetModelByUniqueId(string uniqueId)
        {
            BaseModel model = null;
            if (this.modelsHashtable.ContainsKey(uniqueId))
            {
                model = this.modelsHashtable[uniqueId] as BaseModel;
            }

            return model;
        }

        /// <summary>
        /// Collapse all the models.
        /// </summary>
        public void CollapseModels()
        {
            foreach (string key in this.modelsHashtable.Keys)
            {
                BaseModel model = this.modelsHashtable[key] as BaseModel;
                if (model != null && !model.IsCompositeActivityModel && !model.IsCompositeBlackBoxModel)
                {
                    model.IsCollapsed = true;
                }
            }
        }

        /// <summary>
        /// Create a clone of the required object by serializing and deserializing back.
        /// </summary>
        public static TridentWorkflowModel CreateClone(TridentWorkflowModel model)
        {
            TridentWorkflowModel clone = null;
            try
            {
                using (Stream stream = new MemoryStream())
                {
                    System.Runtime.Serialization.IFormatter formatter =
                        new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

                    // Serialize the stream.
                    formatter.Serialize(stream, model);

                    stream.Position = 0;

                    // Deserialize to create a clone.
                    clone = formatter.Deserialize(stream) as TridentWorkflowModel;
                    SetParentChildRelationship(clone);
                }
            }
            catch (System.Runtime.Serialization.SerializationException)
            {
            }

            return clone;
        }

        /// <summary>
        /// Create the workflow parameter info details.
        /// </summary>
        /// <returns>The parameter info object.</returns>
        public WorkflowActivityParametersInfo CreateWorkflowParameterInfo()
        {
            WorkflowActivityParametersInfo workflowParamInfo = new WorkflowActivityParametersInfo();
            workflowParamInfo.Parameters = TridentWorkflowModel.GetParameterInfoFromActivity(this.Root);
            return workflowParamInfo;
        }

        /// <summary>
        /// Create the activity info object.
        /// </summary>
        /// <param name="activityModel">The activity model for which the info is to be built.</param>
        /// <returns>Collection of all activity info for the activity.</returns>
        private static Collection<ActivityParameterInfo> GetParameterInfoFromActivity(BaseModel activityModel)
        {
            Collection<ActivityParameterInfo> parameterList = new Collection<ActivityParameterInfo>();

            // Retrieve parameters for the activity.
            if (activityModel.IsSimpleActivityModel || activityModel.IsCompositeActivityModel)
            {
                parameterList = TridentWorkflowModel.GetParameterInfo(activityModel);
            }

            // Recurse through the children and get their info.
            if (activityModel.IsSubsectionModel || activityModel.IsCompositeActivityModel || activityModel.IsCompositeBlackBoxModel)
            {
                BaseCompositeModel compositeModel = activityModel as BaseCompositeModel;

                foreach (BaseModel childModel in compositeModel.Children)
                {
                    Collection<ActivityParameterInfo> childActivityList = TridentWorkflowModel.GetParameterInfoFromActivity(childModel);

                    foreach (ActivityParameterInfo childInfo in childActivityList)
                    {
                        parameterList.Add(childInfo);
                    }
                }
            }

            return parameterList;
        }

        /// <summary>
        /// Retrieve parameter info from the activity.
        /// </summary>
        /// <param name="activityModel">The activity model.</param>
        /// <returns>The parameter info for the activity.</returns>
        private static Collection<ActivityParameterInfo> GetParameterInfo(BaseModel activityModel)
        {
            Collection<ActivityParameterInfo> parameterInfoList = new Collection<ActivityParameterInfo>();

            foreach (ParameterDescriptionModel descriptionModel in activityModel.InputParameters)
            {
                // If the parameter is mandatory and is not databound then add it to the list.
                if (descriptionModel.IsMandatoryForWorkflow && !descriptionModel.IsDatabound)
                {
                    ActivityParameterInfo paramInfo = new ActivityParameterInfo();
                    paramInfo.Name = descriptionModel.PropertyName;
                    paramInfo.ActivityParameterInfoType = descriptionModel.DataType;
                    paramInfo.ActivityUniqueName = activityModel.UniqueId;
                    if (descriptionModel.Value != null)
                    {
                        paramInfo.Value = descriptionModel.Value.ToString();
                    }

                    parameterInfoList.Add(paramInfo);
                }
            }

            return parameterInfoList;
        }

        /// <summary>
        /// Get the workflow required parameters from the workflow.
        /// </summary>
        /// <returns>Collection containing the required parameters</returns>
        public Collection<ParameterDescriptionModel> GetWorkflowRequiredParameters()
        {
            Collection<ParameterDescriptionModel> parameters = new Collection<ParameterDescriptionModel>();
            if (this.root != null)
            {
                BaseModel.GetWorkflowRequiredParameters(this.Root, parameters);
            }
            return parameters;
        }


        /// <summary>
        /// Get the workflow output parameters from the workflow.
        /// </summary>
        /// <returns>Collection containing the workflow output parameters</returns>
        public Collection<ParameterDescriptionModel> GetWorkflowOutputParameters()
        {
            Collection<ParameterDescriptionModel> parameters = new Collection<ParameterDescriptionModel>();
            if (this.root != null)
            {
                this.GetWorkflowOutputParameters(this.Root, parameters);
            }
            return parameters;
        }

        /// <summary>
        /// Recurses through the model to fetch the workflow output parameters.
        /// </summary>
        /// <param name="baseModel">The model.</param>
        /// <param name="outputList">The output list.</param>
        private void GetWorkflowOutputParameters(BaseModel baseModel, Collection<ParameterDescriptionModel> outputList)
        {
            // Retrieve parameters for the activity.
            if (baseModel.IsSimpleActivityModel || baseModel.IsCompositeActivityModel)
            {
                var mandatoryList = from param in baseModel.OutputParameters
                                    where param.IsMandatoryForWorkflow
                                    select param;

                foreach (ParameterDescriptionModel requiredModel in mandatoryList)
                {
                    requiredModel.PropertyPaneDisplayName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", baseModel.Name, requiredModel.PropertyName);
                    outputList.Add(requiredModel);
                }
            }

            // Recurse through the children and get their info.
            if (baseModel.IsSubsectionModel || baseModel.IsCompositeActivityModel || baseModel.IsCompositeBlackBoxModel)
            {
                BaseCompositeModel compositeModel = baseModel as BaseCompositeModel;

                foreach (BaseModel childModel in compositeModel.Children)
                {
                    this.GetWorkflowOutputParameters(childModel, outputList);
                }
            }
        }

        /// <summary>
        /// Changes all the activity execution status to faulting
        /// </summary>
        public void ChangeAllExecutingStatusToFaulting()
        {
            foreach (BaseModel model in this.modelsHashtable.Values)
            {
                SimpleActivityModel simpleModel = null;
                CompositeActivityModel compositeModel = null;
                if (model.IsSimpleActivityModel)
                {
                    simpleModel = model as SimpleActivityModel;
                    if (simpleModel.CurrentTridentActivityExecutionStatus == TridentActivityExecutionStatus.Executing)
                    {
                        simpleModel.CurrentTridentActivityExecutionStatus = TridentActivityExecutionStatus.Faulting;
                    }
                }
                else if (model.IsCompositeActivityModel)
                {
                    compositeModel = model as CompositeActivityModel;
                    if (compositeModel.CurrentTridentActivityExecutionStatus == TridentActivityExecutionStatus.Executing)
                    {
                        compositeModel.CurrentTridentActivityExecutionStatus = TridentActivityExecutionStatus.Faulting;
                    }
                }
            }
        }

        /// <summary>
        /// Validate whether the old bindings in the blackbox workflow are still valid.
        /// </summary>
        public void ValidateBlackboxBindings()
        {
            foreach (string uniqueId in this.modelsHashtable.Keys)
            {
                BaseModel model = this.modelsHashtable[uniqueId] as BaseModel;

                // If blackbox then retreive the model and check the bindings.
                if (model.IsSimpleBlackBoxModel || model.IsCompositeBlackBoxModel)
                {
                    Activity blackboxActivity = this.BlackBoxActivity.GetActivityByName(model.UniqueId);

                    if (blackboxActivity != null)
                    {
                        this.ValidateBindings(blackboxActivity);
                    }
                }
            }
        }

        /// <summary>
        /// Merge the workflow values with the ones from the Job Required parameters.
        /// </summary>
        /// <param name="workflowJob">The workflow job instance.</param>
        public void SetWorkflowParameters(Job workflowJob)
        {
            ActivityInstanceParameterCollection<ActivityInstance> reqParameters = workflowJob.ActivityInstances[0].ActivityInstanceParameters;

            // Iterate through the parameters.
            foreach (ActivityInstanceParameter parameter in reqParameters)
            {
                if (!this.ModelsHashtable.ContainsKey(parameter.ParameterAssignment.Instance.Name))
                {
                    continue;
                }

                // Get the activity to which the parameter belongs to.
                BaseModel activityModel = this.ModelsHashtable[parameter.ParameterAssignment.Instance.Name] as BaseModel;

                if (activityModel != null)
                {
                    foreach (ParameterDescriptionModel paramModel in activityModel.InputParameters)
                    {
                        // If the property name matches then update the value.
                        if (paramModel.PropertyName.Equals(parameter.ParameterAssignment.ActivityParameter.Name, StringComparison.Ordinal) &&
                            paramModel.DataType.Equals(parameter.ParameterAssignment.ActivityParameter.Type, StringComparison.Ordinal))
                        {
                            paramModel.Value = parameter.ParamValue;
                            break;
                        }
                    }
                }
            }
        }

        #endregion Public Functions

        #region Private Methods

        /// <summary>
        /// Fired on click of the swap of subsection states.
        /// </summary>
        /// <param name="sender">
        /// State toggle button.
        /// </param>
        /// <param name="e">
        /// EventArgs.
        /// </param>
        private void OnSwapSubsectionState(object sender, EventArgs e)
        {
            if (this.SwapSubsectionState != null)
            {
                this.SwapSubsectionState.Invoke(sender, e);
            }
        }

        /// <summary>
        /// Binds the swap subsection state event handler.
        /// </summary>
        /// <param name="baseCompositeModel">The base composite model.</param>
        private void BindSwapSubsectionStateEventHandler(BaseCompositeModel baseCompositeModel)
        {
            if (baseCompositeModel != null && baseCompositeModel.IsSubsectionModel)
            {
                TridentSubsectionModel subsection = baseCompositeModel as TridentSubsectionModel;
                subsection.SwapSubsectionState += new EventHandler(this.OnSwapSubsectionState);
            }

            foreach (BaseModel model in baseCompositeModel.Children)
            {
                BaseCompositeModel composite = model as BaseCompositeModel;
                if (composite != null)
                {
                    this.BindSwapSubsectionStateEventHandler(composite);
                }
            }
        }

        /// <summary>
        /// On the activity models collections being changed.
        /// </summary>
        /// <param name="sender">
        /// Root activity model.
        /// </param>
        /// <param name="e">
        /// NotifyCollectionChangedEventArgs.
        /// </param>
        private void OnRootActivityModelsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (object addedItem in e.NewItems)
                {
                    BaseModel newModel = addedItem as BaseModel;
                    if (newModel != null)
                    {
                        this.UpdateModelsHash(newModel);
                    }
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (object deletedItem in e.OldItems)
                {
                    BaseModel deletedModel = deletedItem as BaseModel;
                    if (deletedModel != null)
                    {
                        this.DeleteModelFromHashtable(deletedModel);
                    }
                }
            }
        }

        /// <summary>
        /// Delete the model from the hashtable when removed from the workflow model.
        /// </summary>
        /// <param name="modelToDelete">The model to be deleted.</param>
        private void DeleteModelFromHashtable(BaseModel modelToDelete)
        {
            // Remove model from hashtable.
            if (this.modelsHashtable.ContainsKey(modelToDelete.UniqueId))
            {
                this.modelsHashtable.Remove(modelToDelete.UniqueId);

                // Remove the associated assemblies of that model.
                Collection<AssemblyInfoModel> assemblyModelsToDelete = new Collection<AssemblyInfoModel>(this.workflowAssociatedAssemblies
                        .Where(assembly => (assembly.HolderModel != null && assembly.HolderModel.UniqueId.Equals(modelToDelete.UniqueId))).ToList());
                assemblyModelsToDelete.ForEach(model => this.workflowAssociatedAssemblies.Remove(model));
            }

            // If composite recurse through its children.
            BaseCompositeModel compositeModel = modelToDelete as BaseCompositeModel;
            if (compositeModel != null)
            {
                foreach (BaseModel child in compositeModel.Children)
                {
                    this.DeleteModelFromHashtable(child);
                }
            }
        }

        /// <summary>
        /// Delete from the blackbox activity store.
        /// </summary>
        /// <param name="modelToDelete">The model to be deleted.</param>
        private void DeleteFromReferenceActivity(BaseModel modelToDelete)
        {
            Activity activityToDelete = this.BlackBoxActivity.GetActivityByName(modelToDelete.UniqueId);

            if (activityToDelete != null)
            {
                // Find the parent and remove it from its child collection.
                CompositeActivity parent = activityToDelete.Parent as CompositeActivity;
                if (parent != null)
                {
                    parent.Activities.Remove(activityToDelete);
                }
            }
        }

        /// <summary>
        /// Update the models hash on creation of the workflow.
        /// </summary>
        /// <param name="model">
        /// The model whose entry needs to be updated.
        /// </param>
        private void UpdateModelsHash(BaseModel model)
        {
            if (model != null)
            {
                this.modelsHashtable[model.UniqueId] = model;

                // Update the associated assemblies also.
                model.AssociatedAssemblies.ForEach(assembly => this.workflowAssociatedAssemblies.Add(assembly));
                BaseCompositeModel compActModel = model as BaseCompositeModel;
                if (compActModel != null)
                {
                    foreach (BaseModel childActModel in compActModel.Children)
                    {
                        this.UpdateModelsHash(childActModel);
                    }
                }
            }
        }

        /// <summary>
        /// Validate the bindings in the activity.
        /// </summary>
        /// <param name="activity">The activity to be validated.</param>
        private void ValidateBindings(Activity activity)
        {
            Type activityType = activity.GetType();

            // Retrieve all the static and public properties. Dependency properties are usually static and public.
            FieldInfo[] fieldInfoArray = activityType.GetFields(BindingFlags.Static | BindingFlags.Public);

            // Iterate through the property list and check whether it is a dependency properpty.
            for (int iterator = 0; iterator < fieldInfoArray.Length; iterator++)
            {
                if (fieldInfoArray[iterator].FieldType == typeof(DependencyProperty))
                {
                    DependencyProperty depProp = fieldInfoArray[iterator].GetValue(activity) as DependencyProperty;
                    if (depProp != null)
                    {
                        // Check if the binding is set for the property.
                        if (activity.IsBindingSet(depProp))
                        {
                            ActivityBind binding = activity.GetBinding(depProp);

                            // If the activity to which the properpty is bound to is deleted then invalidate the binding.
                            if (!this.modelsHashtable.ContainsKey(binding.Name))
                            {
                                activity.RemoveProperty(depProp);
                            }
                        }
                    }
                }
            }
        }

        #endregion
    }

    /// <summary>
    /// Enumeration for the workflow type.
    /// </summary>
    public enum WorkflowType
    {
        StateMachineWorkflow,
        SequentialWorkflow
    }
}
